#include "CGUIIcon.h"
#include "IGUIFont.h"
#include "IGUISpriteBank.h"
#include "IGUISkin.h"
#include "IGUIEditBox.h"
#include "IGUIStaticText.h"
#include "IGUIEnvironment.h"
#include "SColor.h"
#include "ITexture.h"

// just using a bunch of defines for now, until skins allow custom values
#define SKIN_ICON_WIDTH  32
#define SKIN_ICON_HEIGHT 32
#define SKIN_SMALL_ICON_WIDTH  16
#define SKIN_SMALL_ICON_HEIGHT 16
#define SKIN_ICON_SPACING_WIDTH 64
#define SKIN_ICON_SPACING_HEIGHT 96
#define SKIN_THUMBNAIL_ICON_WIDTH  96
#define SKIN_THUMBNAIL_ICON_HEIGHT 96
#define SKIN_THUMBNAIL_ICON_SPACING_WIDTH 128
#define SKIN_THUMBNAIL_ICON_SPACING_HEIGHT 128

namespace irr {
namespace gui {

//! Constructor
CGUIIcon::CGUIIcon(IGUIEnvironment *env, IGUIElement *parent, s32 id, core::position2d<s32> pos,
				   IGUISpriteBank *sprites, wchar_t *caption)
: IGUIElement((EGUI_ELEMENT_TYPE) EGIEFT_ICON, env, parent, id, 
			  core::rect<s32>(pos.X, pos.Y,
							  pos.X + SKIN_ICON_SPACING_WIDTH, pos.Y + SKIN_ICON_SPACING_HEIGHT)), 
  Sprites(sprites), Font(0), TextBox(0), StaticText(0), 
  Layout(EGUIIL_STANDARD), Selected(false), Animate(true), Editable(true), 
  DrawBorder(false), Draggable(true), Dragging(false),
  NormalSprite(0), PressedSprite(0), 
  UnselectedColor(255,255,255,255), SelectedColor(255,128,128,255), DisabledColor(255,128,128,128), 
  FontColor(255,0,0,0),
  lastClickTime(0), DoubleClickTime(200), Texture(0)
{
	if (Sprites)
		Sprites->grab();

	s32 TextHeight = 10;

	if (Environment && Environment->getSkin())
	{
		Font = Environment->getSkin()->getFont();
		if (Font)
			Font->grab();
	}

	TextBox = Environment->addEditBox(caption, 
		core::rect<s32>(0,SKIN_ICON_HEIGHT, SKIN_ICON_WIDTH,SKIN_ICON_HEIGHT+TextHeight), false, this, -1);

	if (TextBox)
	{
		TextBox->grab();
		TextBox->setSubElement(true);
		TextBox->setAlignment(EGUIA_CENTER, EGUIA_CENTER, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
		if (Font)
			TextBox->setOverrideFont(Font);
		TextBox->setOverrideColor(FontColor);
		TextBox->setWordWrap(true);
		TextBox->setTextAlignment(EGUIA_CENTER, EGUIA_UPPERLEFT);
		//TextBox->setAutoSize(true);
		TextBox->setMaxSize(core::dimension2di(SKIN_ICON_SPACING_WIDTH, SKIN_ICON_SPACING_HEIGHT));
		TextBox->setDrawBorder(true);
		TextBox->enableOverrideColor(true);
		TextBox->setVisible(false);
	}

	StaticText = Environment->addStaticText(caption, 
		core::rect<s32>(0,SKIN_ICON_HEIGHT, SKIN_ICON_WIDTH,SKIN_ICON_HEIGHT+TextHeight), false, true, this);
	if (StaticText)
	{
		StaticText->grab();
		StaticText->setAlignment(EGUIA_CENTER, EGUIA_CENTER, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
		StaticText->setSubElement(true);
		StaticText->setOverrideColor(FontColor);
		StaticText->enableOverrideColor(true);
		StaticText->setBackgroundColor(SelectedColor);
	}

	setText(caption);
	setSelected(false);
}

//! Destructor
CGUIIcon::~CGUIIcon()
{
	if (Sprites)
		Sprites->drop();
	if (Font)
		Font->drop();
	if (TextBox)
		TextBox->drop();
	if (StaticText)
		StaticText->drop();
}

// standard GUI functions
bool CGUIIcon::OnEvent(SEvent e)
{
	u32 time = 111110; // get current time

	switch (e.EventType)
	{
	case EET_GUI_EVENT:
	{
		// absorb any gui events from children if disabled
		if (!IsEnabled)
			return true;

		// button pressed or text clicked.
		switch (e.GUIEvent.EventType)
		{
		case EGET_EDITBOX_ENTER:
		{
			finishEditing();
			return true;
		}
		case EGET_ELEMENT_FOCUS_LOST:
		{
			if (e.GUIEvent.Caller == TextBox)
			{
				if (TextBox->isVisible())
					finishEditing();
			}
			else if (e.GUIEvent.Caller == this || e.GUIEvent.Caller == StaticText)
			{
				setSelected(false);
			}
			break;
		}
		case EGET_ELEMENT_FOCUSED:
			return false;
		default:
			// eat all other gui events
			return true;
		}
/*
			if clicked on the edit box, and it's too late to be classed as a double click, 
			and we're allowed to edit: call startEditing

			otherwise, if someone clicked on the button or the text:

				1) save this time: this is the last time that someone clicked
				2) if it's within double click time, raise a double click event
*/

		break;
	}
	case EET_KEY_INPUT_EVENT:
	{
		if (e.KeyInput.PressedDown)
		{
			switch(e.KeyInput.Key)
			{
				case KEY_F2:
				{
					startEditing();
					return true;
				}
				case KEY_ESCAPE:
				{
					if (TextBox->isVisible())
					{
						TextBox->setText(StaticText->getText());
						finishEditing();
						return true;
					}
					break;
				}
				case KEY_SPACE:
				{
					setSelected(!getSelected());
					return true;
				}
			}
		}
		break;
	}
	case EET_MOUSE_INPUT_EVENT:
	{
		switch(e.MouseInput.Event)
		{
		case EMIE_LMOUSE_PRESSED_DOWN:
		{
			
			core::position2di pos(e.MouseInput.X,e.MouseInput.Y);

			if (!IsEnabled)
				return true;

			lastClickTime = time;
			DragStart = pos;

			if (!Selected)
			{
				// we just clicked on this icon.
				// send click event
			}

			// did the user click the text area?
			if ( TextBox && TextBox->isPointInside(pos))
			{
				if (Selected && time < lastClickTime + DoubleClickTime)
				{
					startEditing();
					break;
				}
				else
				{
					setSelected(true);
				}
				
			}
			else
			{
				setSelected(true);
			}

			break;
		}
		default:
			break;
		}

		break;
	}
	default:
		break;

	}
/*
	// get the current time for working out double clicks (os::Timer::getRealTime())

	// Keyboard events:
		// F2 key is the same as starting editing above
		// return or enter keys are the same as double click

	// mouse events:
		// mouse down, remember that the mouse is pressed
		// mouse move. if the mouse is pressed, we pass a "start drag" event to the parent and remember we are dragging
		// mouse up. if we are dragging, we pass a "stop drag" event to the parent

	*/
	return IGUIElement::OnEvent(e);
}

// 
void CGUIIcon::draw()
{
	u32 time = 0;

	// draw border?
	if (DrawBorder && Environment->getSkin())
		Environment->getSkin()->draw3DSunkenPane(this, 
			video::SColor(), true, false, iconRect + AbsoluteRect.UpperLeftCorner, 
			Clip ? &AbsoluteClippingRect : 0);

	// only draw static text border if we have the focus
	StaticText->setDrawBorder(Environment->getFocus() == this);

	// are we drawing a texture, or a sprite?
	if (Texture && (Layout == EGUIIL_THUMBNAIL || Layout == EGUIL_SLOT))
	{
		// draw texture in sprite area
	}
	else
	if (Sprites)
	{
		Sprites->draw2DSprite( Selected ? PressedSprite : NormalSprite, 
			iconRect.getCenter() + AbsoluteRect.UpperLeftCorner, 
			Clip ? &AbsoluteClippingRect : 0, 
			IsEnabled ? (Selected ? SelectedColor : UnselectedColor) : DisabledColor, 
			lastClickTime, 
			Animate ? time : lastClickTime,
			false,
			true);
	}

	IGUIElement::draw();
}

void CGUIIcon::setEnabled(bool enabled)
{
	finishEditing();
	IGUIElement::setEnabled(enabled);
}

void CGUIIcon::setText(const wchar_t *text)
{
	if (TextBox)
		TextBox->setText(text);
	if (StaticText)
		StaticText->setText(text);
	// todo: if text is longer than displayed text
	//	setToolTipText(text);
	// else
	//  setToolTipText(L"");
}

const wchar_t* CGUIIcon::getText()
{
	return StaticText->getText();
}

bool CGUIIcon::isPointInside(const core::position2d<s32>& point) const
{
	if ( (iconRect + AbsoluteRect.UpperLeftCorner).isPointInside(point) ||
		TextBox->isPointInside(point) || StaticText->isPointInside(point) )
	{
		return true;
	}
	return false;
}
// get/set functions

void CGUIIcon::setLayout(EGUI_ICON_LAYOUT layout)
{
	Layout = layout;

	core::rect<s32> tr;

	switch (Layout)
	{
		case EGUIIL_STANDARD:
		{
			s32 sideSpace = (SKIN_ICON_SPACING_WIDTH - SKIN_ICON_WIDTH) / 2;
			iconRect.UpperLeftCorner.X = sideSpace;
			iconRect.UpperLeftCorner.Y = 0;
			iconRect.LowerRightCorner.X = SKIN_ICON_SPACING_WIDTH - sideSpace;
			iconRect.LowerRightCorner.Y = SKIN_ICON_HEIGHT;
			tr.UpperLeftCorner.X = 0;
			tr.UpperLeftCorner.Y = SKIN_ICON_HEIGHT+1;
			tr.LowerRightCorner.X = SKIN_ICON_SPACING_WIDTH - 2;
			tr.LowerRightCorner.Y = SKIN_ICON_HEIGHT+1 + getTextHeight();

			StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_UPPERLEFT);
			StaticText->setWordWrap(true);

			break;
		}
		case EGUIIL_LIST:
		{
			s32 h = getTextHeight(), 
				mh = h > SKIN_SMALL_ICON_HEIGHT ? h : SKIN_SMALL_ICON_HEIGHT;
			iconRect.UpperLeftCorner.X = 0;
			iconRect.UpperLeftCorner.Y = (mh - SKIN_SMALL_ICON_HEIGHT) / 2;
			iconRect.LowerRightCorner.X = SKIN_SMALL_ICON_WIDTH;
			iconRect.LowerRightCorner.Y = SKIN_SMALL_ICON_HEIGHT + iconRect.UpperLeftCorner.Y;

			tr.UpperLeftCorner.X = SKIN_SMALL_ICON_WIDTH+1;
			tr.UpperLeftCorner.Y = (mh - h) / 2 ;
			tr.LowerRightCorner.X = SKIN_ICON_SPACING_WIDTH - 2;
			tr.LowerRightCorner.Y = (mh - h)/2 + h;

			StaticText->setTextAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
			StaticText->setWordWrap(false);

			break;
		}
		case EGUIIL_THUMBNAIL:
		{
			iconRect.UpperLeftCorner.X = 0;
			iconRect.UpperLeftCorner.Y = 0;
			iconRect.LowerRightCorner.X = SKIN_THUMBNAIL_ICON_WIDTH;
			iconRect.LowerRightCorner.Y = SKIN_THUMBNAIL_ICON_HEIGHT;
			tr.UpperLeftCorner.X = 0;
			tr.UpperLeftCorner.Y = SKIN_THUMBNAIL_ICON_HEIGHT+1;
			tr.LowerRightCorner.X = SKIN_THUMBNAIL_ICON_WIDTH - 2;
			tr.LowerRightCorner.Y = SKIN_THUMBNAIL_ICON_HEIGHT+1 + getTextHeight();

			StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_UPPERLEFT);
			StaticText->setWordWrap(true);

			break;
		}
		case EGUIL_SLOT:
		{
			// todo: set to size of parent
		}
	}
	
	core::rect<s32> totalRect(iconRect.UpperLeftCorner + RelativeRect.UpperLeftCorner, 
							  iconRect.LowerRightCorner + RelativeRect.UpperLeftCorner);

	totalRect.addInternalPoint(tr.UpperLeftCorner + RelativeRect.UpperLeftCorner);
	totalRect.addInternalPoint(tr.LowerRightCorner + RelativeRect.UpperLeftCorner);

	setRelativePosition(totalRect);
	TextBox->setRelativePosition(tr);
	StaticText->setRelativePosition(tr);
}

EGUI_ICON_LAYOUT CGUIIcon::getLayout()
{
	return Layout;
}

void CGUIIcon::setSelected(bool selected)
{
	Selected = selected;
	if (StaticText)
		StaticText->setDrawBackground(Selected);
	setLayout(Layout);
}

bool CGUIIcon::getSelected()
{
	return Selected;
}

void CGUIIcon::setAnimated(bool animate)
{
	Animate = animate;
}

bool CGUIIcon::getAnimated()
{
	return Animate;
}

void CGUIIcon::setEditable(bool editable)
{
	Editable = editable;
}

bool CGUIIcon::getEditable()
{
	return Editable;
}

void CGUIIcon::setSpriteBank(IGUISpriteBank *sprites)
{
	if (Sprites)
		Sprites->drop();

	Sprites = sprites;

	if (sprites)
	{
		sprites->grab();
	}
}

IGUISpriteBank* CGUIIcon::getSpriteBank()
{
	return Sprites;
}

void CGUIIcon::setSprites(s32 normal, s32 pressed)
{
	NormalSprite = normal;
	if (pressed==-1)
		PressedSprite = normal;
	else
		PressedSprite = pressed;
}

void CGUIIcon::setUnselectedColor(video::SColor col)
{
	UnselectedColor = col;
}

void CGUIIcon::setSelectedColor(video::SColor col)
{
	SelectedColor = col;
}

void CGUIIcon::setDisabledColor(video::SColor col)
{
	DisabledColor = col;
}

void CGUIIcon::setFont(IGUIFont* font)
{
	if (Font)
		Font->drop();
	
	Font = font;

	if (Font)
		Font->grab();

	TextBox->setOverrideFont(font);
	
	setLayout(Layout);
}

void CGUIIcon::setFontColor(video::SColor col)
{
	FontColor = col;
	TextBox->setOverrideColor(col);
}

void CGUIIcon::setDrawBorder(bool draw)
{
	DrawBorder = draw;
}

void CGUIIcon::setDraggable(bool draggable)
{
	Draggable = draggable;
}


void CGUIIcon::setTexture(video::ITexture* tex)
{
	if (Texture)
		Texture->drop();

	Texture = tex;

	if (Texture)
		Texture->grab();

}
video::ITexture* CGUIIcon::getTexture()
{
	return Texture;
}

// Action causing functions

// start editing the text in the text box
void CGUIIcon::startEditing()
{
	if (Editable && TextBox)
	{
		Selected = false;
		Environment->setFocus(TextBox);
		TextBox->setVisible(true);
		if (StaticText)
			StaticText->setVisible(false);
	}
}

void CGUIIcon::finishEditing()
{
	if (StaticText)
		StaticText->setVisible(true);

	if (TextBox && TextBox->isVisible())
	{
		TextBox->setVisible(false);
		Environment->removeFocus(TextBox);
		setText(TextBox->getText());
		setSelected(true);
		Environment->setFocus(this);
	}
}


s32 CGUIIcon::getTextHeight()
{
	s32 p = 4;
	if (Environment->getSkin())
		p = Environment->getSkin()->getSize(EGDS_TEXT_DISTANCE_Y) * 2;

	if ((Selected || Environment->getFocus() == this) && Layout != EGUIIL_LIST)
		return StaticText->getTextHeight() + p;
	else
		return Font->getDimension(L"A").Height + p;
}


} // namespace gui
} // namespace irr
